<?php
// 应用公共文件

// 应用公共文件
use \Firebase\JWT\JWT;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\Key;
use think\facade\Db;
use think\facade\Log;



//生成验签
function signToken($user)
{
    $key = '!@#$%*&';         //这里是自定义的一个随机字串，应该写在config文件中的，解密时也会用，相当    于加密中常用的 盐  salt
    $token = array(
        "iss" => $key,        //签发者 可以为空
        "aud" => '',          //面象的用户，可以为空
        "iat" => time(),      //签发时间
        "nbf" => time(),    //在什么时候jwt开始生效  （立即生效）
        "exp" => time() + 604800, //token 过期时间
        "data" => [           //记录的userid的信息，这里是自已添加上去的，如果有其它信息，可以再添加数组的键值对
            'user' => $user,
        ]
    );
    //  print_r($token);
    $jwt = JWT::encode($token, $key, "HS256");  //根据参数生成了 token
    return $jwt;
}


//验证token
function checkToken($token)
{
   
    $key = '!@#$%*&';
    $status = array("code" => 2);
    try {
//        JWT::$leeway = 60;//当前时间减去60，把时间留点余地
        $decoded = JWT::decode($token, new Key($key, 'HS256')); //HS256方式，这里要和签发的时候对应
        $arr = (array)$decoded;
        $res['code'] = 1;
        $res['data'] = $arr['data'];
        return $res;

    } catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
        $status['msg'] = "签名不正确";
        return $status;
    } catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
        $status['msg'] = "token失效";
        return $status;
    } catch (\Firebase\JWT\ExpiredException $e) { // token过期
        $status['msg'] = "token过期";
        return $status;
    } catch (Exception $e) { //其他错误
        $status['msg'] = "未知错误";
        return $status;
    }
}

//生成进货单号
//function generateOrderNumber($companyId)
//{
//    // 前缀
//    $prefix = 'JH';
//    // 获取当前时间戳
//    $timestamp = date('YmdHis'); // 格式为YYYYMMDDHHMMSS
//
//    // 获取当前公司的最大进货单号
//    $maxOrderNumber = Db::table('PurchaseBill')->where('PurchaseBill_company_id', $companyId)->max('PurchaseBill_BillNumber');
//
//    // 如果没有进货单号，序列号从001开始
//    if (empty($maxOrderNumber)) {
//        $sequence = '001';
//    } else {
//        // 提取序列号部分
//        $maxSequence = str_replace($prefix . $timestamp, '', $maxOrderNumber);
//
//        // 获取下一个序列号，即当前最大序列号加一
//        $sequence = intval($maxSequence) + 1;
//
//        // 确保序列号始终是三位数，不足前面补零
//        $sequence = sprintf('%03d', $sequence);
//    }
//
//    // 组合新的进货单号，包括公司ID、时间戳和序列号
//    $newOrderNumber = $prefix . $timestamp . $companyId . $sequence;
//
//    return $newOrderNumber;
//}
//单号生成方法
function generateOrderNumber($companyId,$prefix,$table)
{
    // 前缀
    //$prefix = 'JH';
    // 获取当前日期（年月日）
    $date = date('Ymd');
    // 当天已生成的最大序列号
    $maxSequence = 0;

    // 查询数据库以获取当天该公司的最大序列号
    $maxOrderNumber = Db::table($table)
        ->where($table.'_company_id', $companyId)
        ->where($table.'_BillNumber', 'like', $prefix . $date . '%') // 移除点（.）作为模式匹配的一部分
        ->field("SUBSTR(".$table."_BillNumber, LENGTH('$prefix$date') + 1) as max_sequence") // 从前缀和日期之后截取序列号
        ->order('max_sequence', 'desc')
        ->find();

    // 如果没有找到进货单号或者没有序列号部分，序列号从00001开始
    if (empty($maxOrderNumber) || empty($maxOrderNumber['max_sequence'])) {
        $maxSequence = 0;
    } else {
        $maxSequence = intval($maxOrderNumber['max_sequence']);
    }

    // 获取下一个序列号，即当前最大序列号加一
    $nextSequence = $maxSequence + 1;

    // 确保序列号始终是五位数，不足前面补零
    $sequence = sprintf('%05d', $nextSequence); // 假设序列号总长度是5位

    // 组合新的进货单号，包括前缀、日期和序列号
    $newOrderNumber = $prefix . $date . $sequence;

    return $newOrderNumber;
}

// 生成条码的函数
 function generateBarcode($companyId)
{
    // 获取当前公司的最新条码记录
    $latestBarcode = Db::table('assets')
        ->where('assets_company_id', $companyId)
        ->order('assets_id', 'desc') // 假设按照记录ID降序排列
        ->find();

    $sequence = 1; // 初始序列号
    if ($latestBarcode) {
        // 如果存在记录，则序列号加1
        $sequence = (int)substr($latestBarcode['assets_code'], 4) + 1;
    }

    // 格式化序列号，确保为8位数字
    $formattedSequence = str_pad($sequence, 8, '0', STR_PAD_LEFT);

    // 假设的校验位计算规则（这里需要根据实际规则进行修改）
    //$checkDigit = calculateCheckDigit($companyId . $formattedSequence);

    // 生成完整的条码
    $barcode =  $companyId . $formattedSequence;

//    // 将新条码存储到数据库
//    Db::table('barcode_table')->insert([
//        'company_id' => $companyId,
//        'barcode' => $barcode,
//        // 其他字段...
//    ]);

    return $barcode;
}

// 假设的校验位计算函数（需要根据实际规则实现）
 function calculateCheckDigit($data)
{
    // 这里只是一个示例，需要根据实际的校验规则来实现
    $sum = array_sum(str_split($data));
    $checkDigit = $sum % 10;
    return $checkDigit;
}

// 校验条码的函数
 function validateBarcode($barcode)
{
    // 提取校验位、公司ID和序列号
    $checkDigit = (int)$barcode[0];
    $companyId = substr($barcode, 1, 3);
    $sequence = substr($barcode, 4);

    // 重新计算校验位
    $calculatedCheckDigit = calculateCheckDigit($companyId . $sequence);

    // 比较计算出的校验位和条码中的校验位是否一致
    return $checkDigit === $calculatedCheckDigit;
}

//按钮级权限验证
function ButtonAuth($route,$companyId,$userId)
{

    $rightMap = [
        '/Customer/Insert_CustomerInfo' => 19,  // 新增客户按钮
        '/Customer/Update_CustomerInfo' => 20,  // 修改客户按钮
        '/Customer/Delete_CustomerInfo' => 21,  // 删除客户按钮
        '/Role/Insert_RoleInfo' => 39,          // 新增角色按钮
        '/Role/Update_RoleInfo' => 40,          // 修改角色按钮
        '/Role/Delete_RoleInfo' => 41,          // 删除角色按钮
        '/Role/Update_SetRight' => 51,          // 分配权限按钮
        '/Supplier/Insert_SupplierInfo' => 31,  // 新增供应商按钮
        '/Supplier/Update_SupplierInfo' => 32,  // 修改供应商按钮
        '/Supplier/Delete_SupplierInfo' => 33,  // 删除供应商按钮
        '/User/Insert_UserInfo' => 35,          // 新增用户按钮
        '/User/Update_UserInfo' => 36,          // 修改用户按钮
        '/User/Delete_UserInfo' => 37,          // 删除用户按钮（修正）
        '/Branch/insert_branchinfo' => 15,      // 新增区域按钮
        '/Branch/update_branchinfo' => 16,      // 修改区域按钮
        '/Branch/Delete_BranchInfo' => 17,      // 删除区域按钮
        '/AssetTypes/addAssetTypes' => 64,      // 新增资产分类按钮
        '/AssetTypes/updateAssetTypes' => 65,   // 修改资产分类按钮
        '/AssetTypes/deleteAssetTypes' => 66,   // 删除资产分类按钮
        '/Department/addDepartment' => 67,      // 新增部门按钮
        '/Department/updateDepartment' => 68,   // 修改部门按钮
        '/Department/deleteDepartment' => 69,   // 删除部门按钮
        '/Location/addlocation'=>70,            //新增位置管理按钮
        '/Location/updatelocation'=>71,        //修改位置管理按钮
        '/Location/deletelocation'=>72,        //删除位置管理按钮
        '/AssetsStockin/add_AssetsStockin'=>73, //新增入库单
        '/AssetsStockin/checkImportData'=>74,//excel导入
        '/AssetsStockin/check_Stockin'=>75,//审核入库单
        '/AssetsStockin/update_Stockin'=>76,//修改入库单
        '/AssetsStockin/update_StockinDetail'=>76,//修改入库单明细
        '/AssetsStockin/invalid_Stockin'=>77,//作废入库单
        '/AssetsStockin/delete_Stockin'=>78,//删除入库单
        '/AssetsStockin/uncheck_StockIn'=>79,//反审核入库单
        '/AssetUseBill/add_AssetUseBill'=>80,//新增领用单
        '/AssetUseBill/check_AssetUseBill'=>81,//审核领用单
        '/AssetUseBill/update_AssetUseBill'=>82,//修改领用单
        '/AssetUseBill/invalid_AssetUseBill'=>83,//作废领用单
        '/AssetUseBill/delete_AssetUseBill'=>84,//删除领用单
        '/AssetUseBill/uncheck_AssetUseBill'=>85,//反审核领用单
        '/AssetReturnsBill/add_AssetReturnsBill'=>86,//新增退库单
        '/AssetReturnsBill/check_AssetReturnsBill'=>87,//审核退库单
        '/AssetReturnsBill/update_AssetReturnsBill'=>88,//修改退库单
        '/AssetReturnsBill/invalid_AssetReturnsBill'=>89,//作废退库单
        '/AssetReturnsBill/delete_AssetReturnsBill'=>90,//删除退库单
        '/AssetReturnsBill/uncheck_AssetReturnsBill'=>91,//反审核退库单
        '/InfoUpdateBill/add_InfoUpdateBill'=>97,//新增资产信息修改单
        '/InfoUpdateBill/check_InfoUpdateBill'=>98,//审核资产信息修改单
        '/InfoUpdateBill/update_InfoUpdateBill'=>99,//修改资产信息修改单
        '/InfoUpdateBill/invalid_InfoUpdateBill'=>100,//作废资产信息修改单
        '/InfoUpdateBill/delete_InfoUpdateBill'=>101,//删除资产信息修改单
        '/InfoUpdateBill/uncheck_InfoUpdateBill'=>102,//反审核资产信息修改单
        
    ];

    // 将 $route 转换为小写
    $routeLower = strtolower($route);
    // 将 $rightMap 的键转换为小写键的数组
    $lowerRightMap = array_change_key_case($rightMap, CASE_LOWER);

    // 如果路由不在映射数组中，则不需要权限验证，直接放行
    if (!isset($lowerRightMap[$routeLower])) {

        return true; // 不属于需要权限的页面,直接放行
    }


    // 调用权限检查函数，检查用户是否有对应权限
    return checkUserRight($companyId, $userId, $lowerRightMap[$routeLower]);

}

/**
 * 检查用户是否具有特定权限
 *
 * @param int $companyId 公司ID
 * @param int $userId 用户ID
 * @param int $rightId 权限ID
 * @return bool 是否有权限
 */
function checkUserRight($companyId, $userId, $rightId)
{
    //检查是否为超级管理员
    $rightAuth = Db::table('user')
        ->where('user_id', $userId)
        ->where('user_company_id', $companyId)
        ->where('user_root', 1)
        ->find();

    if($rightAuth){
        return true;
    }
    //普通用户鉴权
    $rightAuth = Db::table('vw_user_role_right')
        ->where('user_id', $userId)
        ->where('user_company_id', $companyId)
        ->where('right_id', $rightId)
        ->where('role_switch', 1)
        ->find();

    return $rightAuth ? true : false;
}

// 定义一个函数来过滤掉对象中的空值键值对
function filterEmptyValues($obj) {
    $result = [];
    foreach ($obj as $key => $value) {
        // 检查值是否为空，这里你可以根据需要添加更多的条件
        if ($value !==" " && $value !== null && $value !== "null" && $value !== "undefined" && $value !== "0" && $value !== 0 && $value !== "") {

            $result[$key] = $value;
        }
    }
    return (array)$result; // 将过滤后的数组转回对象
}

/**
 * 获取指定公司的资产类型树状结构
 *
 * @param int $companyId 公司ID，用于查询该公司下的资产类型
 * @return mixed 返回资产类型树的JSON格式数据，若发生异常返回包含错误信息的JSON
 */
 function getAssetTypesTree($companyId)
{
    try {
        // 查询数据，排除AssetTypes_switch为0的记录，并按level排序
        $data = Db::table('AssetTypes')
            ->where('AssetTypes_switch', 1)
            ->where('AssetTypes_company_id',$companyId)
            ->order('AssetTypes_level', 'asc')
            ->select();

        // 构建层级数据结构

        $tree = buildTree($data);


        return $tree; // 假设返回JSON格式，根据实际需要调整
    } catch (\Exception $e) {
        // 异常处理
        return json(['code' => 500, 'msg' => '获取数据失败: ' . $e->getMessage()]);
    }
}

/**
 * 构建树形结构的递归方法
 *
 * @param Collection|array $items 数据集合
 * @param int|null $parentId 父ID，默认为null表示根节点
 * @return array 树形结构数据
 */
 function buildTree($items, $parentId = null)
{
    $tree = [];
    foreach ($items as $item) {
        //($item);
        if ($item['AssetTypes_parentid'] === $parentId || ($parentId === null && $item['AssetTypes_parentid'] === 0)) {
            // 复制当前节点并移除无用的parent_id字段
            $temp = $item;
            unset($temp['AssetTypes_parentid']);
            unset($temp['AssetTypes_level']);

            // 递归查找子节点
            $temp['children'] = buildTree($items, $item['AssetTypes_id']);

            $tree[] = $temp;
            //var_dump($tree);
        }
    }
    return $tree;
}


//根据传入的单据类型名称AssetHistories_type,单据id AssetHistories_bill_id,获取单据编号
function getBillNumber($type,$id){
    $BillNumber='';
    switch ($type) {
        case '入库':
            $BillNumber=Db::table('instock')
                ->where('instock_id',$id)
                ->value('instock_BillNumber');
            break;
        case '领用':
            $BillNumber=Db::table('AssetUseBill')
                ->where('AssetUseBill_id',$id)
                ->value('AssetUseBill_BillNumber');
            break;

        case '退库':
            $BillNumber=Db::table('AssetReturnsBill')
                ->where('AssetReturnsBill_id',$id)
                ->value('AssetReturnsBill_BillNumber');
            break;
        case '信息修改':
            $BillNumber=Db::table('InfoUpdateBill')
                ->where('AssetHistories_id',$id)
                ->value('AssetHistories_BillNumber');
            }
            return $BillNumber;
 }

 //检查资产是否被其他未审核单据占用
 function checkAssetOccupy($assets,$table,$type){

     //找查资产是否已经被其他未审核单据占用
     foreach ($assets as $asset){
         $findId=Db::table($table.'_assets')->where('assets_id',$asset['assets_id'])->value($table.'_id');
         if ($findId)
         {
             $findAsset=Db::table($table)->where($table.'_id',$findId)->where($table.'_state','未审核')->value($table.'_BillNumber');
             if($findAsset)
             {
                 return json([
                     'code' => 201,
                     'msg' => '资产:'.$asset['assets_name'].' '.'编码:'.$asset['assets_code'].'已经被未审核'.$type.':'.$findAsset.'占用，请先处理此单据'
                 ]);
             }

         }

     }

 }

 //单据反审核前验证资产是否已经产生了其他单据
 function uncheck_checkAssetBill($bill_id,$table,$type){

     //根据领用单id找查AssetUseBill_assets表中对应的所有资产id，再根据这些资产id在AssetHistories表中找对应的历史记录,再根据资产的历史记录一条一条比对，首先找到AssetHistories_bill_id字段对应$AssetUseBill_id的记录，以这条记录AssetHistories_date字段为标准，如果资产有其他记录的时间大于这条记录的时间，则不能反审核，否则可以反审核
     $Bill_assets = Db::table($table.'_assets')
         ->where($table.'_id', $bill_id)
         ->select()->toArray();
     $Bill_assets_id = array_column($Bill_assets, 'assets_id');
     $AssetHistories_date = Db::table('AssetHistories')
         ->where('AssetHistories_bill_id', $bill_id)
         ->where('AssetHistories_assets_id', 'in', $Bill_assets_id)
         ->where('AssetHistories_type',$type)
         ->order('AssetHistories_date', 'desc')
         ->value('AssetHistories_date');

     $AssetHistories_date_one = Db::table('AssetHistories')
         ->where('AssetHistories_bill_id', '<>', $bill_id)
         ->where('AssetHistories_assets_id', 'in', $Bill_assets_id)
         ->where('AssetHistories_date', '>', $AssetHistories_date)
         ->find();
     //打日志
    //Log::record('这是一条信息日志'.$AssetHistories_date_one, 'info',[],true);



     if ($AssetHistories_date_one) {

         //json返回是哪个资产,拼接返回显示资产编号资产名称
         $Asset_one = Db::table('assets')
             ->where('assets_id', $AssetHistories_date_one['AssetHistories_assets_id'])
             ->find();
         $Asset_name = $Asset_one['assets_name'];
         $Asset_number = $Asset_one['assets_code'];
         
         return json([
             'code' => 501,
             'msg' => '资产:'.$Asset_name.' 编号:'.$Asset_number.' 已产生了'.$AssetHistories_date_one['AssetHistories_type'].'单据,单据号:'.getBillNumber($AssetHistories_date_one['AssetHistories_type'],$AssetHistories_date_one['AssetHistories_bill_id']).'请先处理此单据',
             'data' => [
                 'Asset_name' => $Asset_name,
                 'Asset_number' => $Asset_number
             ]
         ]);

     }



}


//判断是否是二进制数据
function isBinaryData($data) {
    // 统计不可打印字符的数量
    $nonPrintableCount = 0;
    $length = strlen($data);

    for ($i = 0; $i < $length; $i++) {
        // 如果字符的 ASCII 值小于 32（控制字符）或大于 126（非 ASCII 字符），则认为是不可打印字符
        if (ord($data[$i]) < 32 || ord($data[$i]) > 126) {
            $nonPrintableCount++;
        }
    }

    // 如果不可打印字符的比例超过一定阈值（例如 30%），则认为是二进制数据
    return ($nonPrintableCount / $length) > 0.3;
}











